﻿using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace DfmPoursor
{
    class Program
    {
        // map des dfm. clé : nom de la classe - valeur : chemin
        private static Dictionary<string, string> dfmMap = new Dictionary<string, string>();

        // liste des classes trouvées dans les subForm à analyser
        private static List<string> classesToParse = new List<string>();

        // liste de KVPair :
        //  -> clé : nom du composant
        //  -> valeur : [0] - type; [1] - cible d'une subForm
        private static Dictionary<string, List<KeyValuePair<string, string[]>>> listeComposants = new Dictionary<string, List<KeyValuePair<string, string[]>>>();

        private static void SubFoldersTreatment(string RootFolder)
        {
            // lecture des fichiers .dfm présents
            foreach (var dfmS in Directory.EnumerateFiles(RootFolder, "*.dfm"))
            {
                {
                    using (var sR = new StreamReader(dfmS))
                    {
                        var ligne1 = sR.ReadLine();
                        if (ligne1.Contains("inherited"))
                        {
                            var classe = ligne1.Replace("inherited ", "").Split(':')[1].Trim();

                            if (classe != "")
                            {
                                if (!dfmMap.ContainsKey(classe))
                                    dfmMap.Add(classe, dfmS);
                            }
                        }
                    }
                }
            }

            // lecture des répertoires
            var listeSousSystemes = Directory.EnumerateDirectories(RootFolder);

            // Parcours des dossiers
            foreach (var ss in listeSousSystemes)
            {
                SubFoldersTreatment(ss);
            }
        }

        static void Main(string[] args)
        {
            if (args.Count() > 0)
            {
                // Deux paramètres : 
                // -> le domaine
                // -> le fichier point d'entrée

                // Récupération du domaine (chemin), et récupération des classes associées avec les chemins concernés
                SubFoldersTreatment(args[0]);

                // Lecture du fichier dfm point d'entrée.
                var firstFile = dfmMap.Where(x => x.Key.Equals(args[1])).First().Value;

                DfmFileMainParsing(firstFile);

                // Lecture des fichiers associés aux classes répertoriées.
                //while (classesToParse.Count > 0)
                //{
                ParseNextClasses();
                //}

                // Fin du traitement. Placement de la liste des composants dans le csv de sortie
                using (var sW = new StreamWriter("sortie.csv"))
                {
                    // première ligne : les entêtes
                    sW.WriteLine("Classe;Nom du composant;Type du composant;Cible");

                    foreach (var KVP in listeComposants)
                    {
                        var values = KVP.Value;
                        foreach (var kvp in values)
                        {
                            sW.WriteLine(KVP.Key.Trim() + ";" + kvp.Key.Trim() + ";" + NormalizedType(kvp.Value[0]) + ";" + kvp.Value[1].Trim());
                        }
                    }

                    sW.Close();
                }
            }
        }

        private static void ParseNextClasses()
        {
            while (classesToParse.Count > 0)
            {
                var file = dfmMap.Where(x => x.Key.Equals(classesToParse[0])).FirstOrDefault().Value;
                if (file != null)
                    DfmFileMainParsing(file);
                classesToParse.RemoveAt(0);
            }
        }

        private static void DfmFileMainParsing(string File)
        {
            using (var sR = new StreamReader(File))
            {
                // gestion de la première ligne qui identifie la classe de la dfm
                var ligne1 = sR.ReadLine();
                var classe = ligne1.Replace("inherited ", "").Split(':')[1];

                if (!listeComposants.ContainsKey(classe))
                {
                    var listeLocalComposants = new List<KeyValuePair<string, string[]>>();
                    bool rechercheSubForm = false;
                    var classeT = "";

                    while (!sR.EndOfStream)
                    {
                        var ligne = sR.ReadLine();

                        if (rechercheSubForm)
                        {
                            if (ligne.Contains("FormClassName"))
                            {
                                classeT = ligne.Split('=')[1].Trim().Replace("'", "");
                                if (!classesToParse.Contains(classeT))
                                    classesToParse.Add(classeT);
                                rechercheSubForm = false;
                            }
                        }
                        else
                        {
                            string nomComposant = "";
                            string typeComposant = "";

                            // Ligne de déclaration de composant
                            if (ligne.Contains("inherited"))
                            {
                                var ligneTronquee = ligne.Replace("inherited ", "").Split(':');
                                nomComposant = ligneTronquee[0].Trim();
                                typeComposant = ligneTronquee[1].Trim();
                            }
                            else if (ligne.Contains("object"))
                            {
                                var ligneTronquee = ligne.Replace("object ", "").Split(':');
                                nomComposant = ligneTronquee[0].Trim();
                                typeComposant = ligneTronquee[1].Trim();
                            }

                            // Gestion des SubForm
                            rechercheSubForm = typeComposant.Contains("TSubForm");

                            // Si un composant a été trouvé, on alimente la liste globale
                            if (nomComposant != "" && typeComposant != "" && ApprouvedComponentType(typeComposant))
                            {
                                KeyValuePair<string, string[]> composant = new KeyValuePair<string, string[]>(nomComposant, new string[] { typeComposant, classeT });
                                listeLocalComposants.Add(composant);
                                classeT = "";
                            }
                        }
                    }

                    listeComposants.Add(classe, listeLocalComposants);
                }
            }
        }

        private static bool ApprouvedComponentType(string aComponentType)
        {
            return (!aComponentType.Contains("Action") &&
                    !aComponentType.Contains("Remote") &&
                    !aComponentType.Contains("TActivity") &&
                    !aComponentType.Contains("TApriClient") &&
                     !aComponentType.Contains("TCacheLevelComp") &&
                    !aComponentType.Contains("TClientDataset") &&
                    !aComponentType.Contains("TClientManager") &&
                    !aComponentType.Contains("TDataNotifier") &&
                    !aComponentType.Contains("TDataSource") &&
                    !aComponentType.Contains("Field") &&
                    !aComponentType.Contains("TDBTreeViewConnector") &&
                    !aComponentType.Contains("TImageList") &&
                    !aComponentType.Contains("TListNotifier") &&
                    !aComponentType.Contains("TMultiDataSet") &&
                    !aComponentType.Contains("TNotifierList") &&
                    !aComponentType.Contains("TPriorAction"));
        }

        private static string NormalizedType(string aComponentType)
        {
            return aComponentType.Split('[')[0].Trim();
        }
    }
}
